package yusvr

import (
	"compress/gzip"
	"context"
	"encoding/json"
	"log"
	"net/http"
	"time"

	"gitee.com/yscsky/yu"
	"gitee.com/yscsky/yu/prome"
	"gitee.com/yscsky/yu/yuresp"
	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"google.golang.org/protobuf/proto"
)

// IEchoServer gin服务实现接口
type IGinServer interface {
	yu.IServer
	Gin() *gin.Engine
	Health()
	Group(path string, handlers ...gin.HandlerFunc) *gin.RouterGroup
	JSON(c *gin.Context, data any)
	GzipJSON(c *gin.Context, data any)
	JsonOK(c *gin.Context, data any)
	JsonErr(c *gin.Context, err error)
	JsonMsg(c *gin.Context, msg string)
	PbOK(c *gin.Context, m proto.Message) error
	GzipPbOK(c *gin.Context, m proto.Message) error
	PbErr(c *gin.Context, err error) error
	Promethous(name, pass string)
	NoCache() gin.HandlerFunc
	BasicAuth(username, password string) gin.HandlerFunc
	LogControl(trace bool, skip []string) gin.HandlerFunc
	CORS(origin, methods, headers, age string) gin.HandlerFunc
	DefaultCORS() gin.HandlerFunc
	PromeMetrics() gin.HandlerFunc
}

// GinServer 包含gin的http server
type ginServer struct {
	name   string
	server *http.Server
	engine *gin.Engine
}

// NewGinServer 创建GinServer
func NewGinServer(name, addr, mod string) IGinServer {
	gin.SetMode(mod)
	gs := &ginServer{
		name:   name,
		server: &http.Server{Addr: addr},
		engine: gin.New(),
	}
	gs.engine.Use(gin.Recovery(), gin.ErrorLogger())
	gs.server.Handler = gs.engine
	return gs
}

// OnStart 实现ServerInterface接口
func (s *ginServer) OnStart() bool {
	yu.Logf("%s gin server start at %s", s.Info(), s.server.Addr)
	if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
		yu.LogErr(err, "ListenAndServe")
	}
	return true
}

// OnStop 实现ServerInterface接口
func (s *ginServer) OnStop() {
	ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer cancel()
	if err := s.server.Shutdown(ctx); err != nil {
		yu.LogErr(err, "Shutdown")
	}
}

// Info 实现ServerInterface接口
func (s *ginServer) Info() string {
	return s.name
}

// Gin 获取Gin
func (s *ginServer) Gin() *gin.Engine {
	return s.engine
}

// Health 健康检查接口，需要的话设置
func (s *ginServer) Health() {
	s.engine.GET("health", func(c *gin.Context) { s.JsonOK(c, "yes") })
}

// Group 创建gin.RouterGroup
func (s *ginServer) Group(path string, handlers ...gin.HandlerFunc) *gin.RouterGroup {
	return s.engine.Group(path, handlers...)
}

// JSON 发送JSON
func (s *ginServer) JSON(c *gin.Context, data any) {
	c.Writer.Header().Set("Content-Type", "application/json;charset=utf-8")
	if err := json.NewEncoder(c.Writer).Encode(data); err != nil {
		http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
		return
	}
}

// GzipJSON gzip压缩发送JSON
func (s *ginServer) GzipJSON(c *gin.Context, data any) {
	c.Writer.Header().Set("Content-Type", "application/json;charset=utf-8")
	c.Writer.Header().Set("Content-Encoding", "gzip")
	gz := gzip.NewWriter(c.Writer)
	defer gz.Close()
	if err := json.NewEncoder(gz).Encode(data); err != nil {
		http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
		return
	}
}

// JsonOK 反馈OK
func (s *ginServer) JsonOK(c *gin.Context, data any) {
	s.JSON(c, yuresp.RespOK(data))
}

// JsonErr 反馈Err
func (s *ginServer) JsonErr(c *gin.Context, err error) {
	s.JSON(c, yuresp.RespErr(err))
}

// JsonMsg 反馈发生错误信息
func (s *ginServer) JsonMsg(c *gin.Context, msg string) {
	s.JSON(c, yuresp.RespMsg(yuresp.CodeErr, msg))
}

// PbOK 反馈OK
func (s *ginServer) PbOK(c *gin.Context, m proto.Message) error {
	return yuresp.PbRespOK(c.Writer, m)
}

// GzipPbOK gzip反馈OK
func (s *ginServer) GzipPbOK(c *gin.Context, m proto.Message) error {
	return yuresp.GzipPbRespOK(c.Writer, m)
}

// PbErr 反馈Err
func (s *ginServer) PbErr(c *gin.Context, err error) error {
	return yuresp.PbRespError(c.Writer, err)
}

// Promethous 启动Promethous监听
func (s *ginServer) Promethous(name, pass string) {
	prome.InitPrometheus("YU", s.name)
	s.engine.GET("metrics", s.BasicAuth(name, pass), s.promethousHandler())
}

// PromethousHandler 启动promethous http监听
func (s *ginServer) promethousHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		promhttp.Handler().ServeHTTP(c.Writer, c.Request)
	}
}

// NoCache 设置请求头为无缓存
func (s *ginServer) NoCache() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Writer.Header().Set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate")
		c.Next()
	}
}

// BasicAuth 设置BasicAuth认证
func (s *ginServer) BasicAuth(username, password string) gin.HandlerFunc {
	return func(c *gin.Context) {
		if u, p, ok := c.Request.BasicAuth(); ok && u == username && p == password {
			c.Next()
			return
		}
		c.Writer.Header().Set("WWW-Authenticate", `Basic realm="Authorization Required"`)
		http.Error(c.Writer, "unauthorized", http.StatusUnauthorized)
		log.Println("[ERR] - unauthorized")
		c.Abort()
	}
}

// LogControl 控制gin是否输出log
func (s *ginServer) LogControl(trace bool, skip []string) gin.HandlerFunc {
	logger := gin.LoggerWithConfig(gin.LoggerConfig{SkipPaths: skip})
	return func(c *gin.Context) {
		if !trace {
			c.Next()
			return
		}
		logger(c)
	}
}

// CORS 设置跨域
func (s *ginServer) CORS(origin, methods, headers, age string) gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
		if methods != "" {
			c.Writer.Header().Set("Access-Control-Allow-Methods", methods)
		}
		if headers != "" {
			c.Writer.Header().Set("Access-Control-Allow-Headers", headers)
		}
		if age != "" {
			c.Writer.Header().Set("Access-Control-Max-Age", age)
		}
		if c.Request.Method != "OPTIONS" {
			c.Next()
		}
	}
}

// DefaultCORS 设置默认跨域
func (s *ginServer) DefaultCORS() gin.HandlerFunc {
	return s.CORS(
		"*",
		"GET, POST, DELETE, PUT",
		"DNT, X-Mx-ReqToken, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization",
		"600",
	)
}

// PromeMetrics prometheus metrics 中间件
func (s *ginServer) PromeMetrics() gin.HandlerFunc {
	return func(c *gin.Context) {
		defer prome.PromeTrace(c.Request.URL.Path)()
		c.Next()
	}
}
